package cn.virens.web.controller;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.virens.Assert;
import cn.virens.common.CloseableUtil;
import cn.virens.database.mysql.model.base.BaseUploadFile;
import cn.virens.exception.ExceptionUtil;
import cn.virens.service.inter.base.upload.BaseUploadFileService;
import cn.virens.web.common.BaseJSONController;
import cn.virens.web.components.spring.exception.NotFileFoundException;

@RestController
public class UploadFileContorller extends BaseJSONController {
	private final static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");

	private @Autowired BaseUploadFileService mBaseImageService;

	@RequestMapping(path = "/down/file/{code}")
	public void down(@PathVariable String code, HttpServletRequest request, HttpServletResponse response) throws IOException {
		OutputStream outputStream = null;
		InputStream inputStream = null;

		try {
			BaseUploadFile file = mBaseImageService.selectOne(code);
			if (file == null) { throw new NotFileFoundException("File", code); }

			long posStart = 0;
			long posEnd = file.getLength() - 1;

			// 获取断点续传的下标位置
			String headerRange = null;
			if ((headerRange = request.getHeader(HttpHeaders.RANGE)) != null) {
				response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
				headerRange = StrUtil.removeAll(headerRange, "bytes=");
				String[] temp = StrUtil.split(headerRange, "-");
				if (temp != null && temp.length == 2) {
					posStart = Convert.toLong(temp[0], posStart);
					posEnd = Convert.toLong(temp[1], posEnd);
				} else if (temp != null && temp.length == 1) {
					posStart = Convert.toLong(temp[0], posStart);
				}
			}

			// 将文件传输到输出流
			File file2 = FileUtil.file(file.getPath(getRootPath(request)));
			if (!file2.exists()) { throw new NotFileFoundException("File", code); }

			// 填充响应头
			StringBuilder builder = new StringBuilder();
			builder.append("bytes");
			builder.append(" ").append(posStart);
			builder.append("-").append(posEnd);
			builder.append("/").append(file.getLength());

			response.reset();
			response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
			response.setHeader(HttpHeaders.CONTENT_TYPE, file.contentType());
			response.setHeader(HttpHeaders.CONTENT_RANGE, builder.toString());
			response.setHeader(HttpHeaders.CONTENT_LENGTH, file.contentLength());
			response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.contentName());

			// 获取输入*输出流
			inputStream = FileUtil.getInputStream(file2);
			outputStream = response.getOutputStream();

			// 跳过已经下载的
			for (int i = 0; i < posStart; i++) {
				inputStream.read();
			}

			// 文件流输出
			int len = 0;
			long index = 0;
			byte[] buffer = new byte[1024];
			while ((len = inputStream.read(buffer)) != -1) {
				if ((index = index + len) > posEnd) {
					outputStream.write(buffer, 0, (int) (posEnd % buffer.length));

					break;
				} else {
					outputStream.write(buffer, 0, len);
				}
			}
		} catch (NotFileFoundException e) {
			response.sendError(404, e.getMessage());
		} catch (IOException e) {
			response.sendError(500, e.getMessage());
		} finally {
			CloseableUtil.close(inputStream);
			CloseableUtil.close(outputStream);
		}
	}

	@RequestMapping(path = "/upload/file.json", method = { RequestMethod.POST, RequestMethod.OPTIONS })
	public void uploadOptions(HttpServletRequest request, HttpServletResponse response) {
		String origin = StrUtil.emptyToDefault(request.getHeader(HttpHeaders.ORIGIN), "*");

		response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET,POST,OPTIONS");
		response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
		response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
		response.setHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "86400");
	}

	@RequestMapping(path = "/upload/file.json", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
	public Model upload(@RequestParam("file") MultipartFile file, HttpServletRequest request, HttpServletResponse response, Model model) {
		this.uploadOptions(request, response);

		try {
			Assert.isNull(file, "文件不存在");

			// 获取存储目录
			String rootPath = getRootPath(request);
			String savePath = getRealPath(request);

			// 获取文件名 & 文件指纹签名
			String fileName = file.getOriginalFilename();
			String fileSign = DigestUtil.md5Hex(file.getBytes());

			// 检查是否已有该文件
			BaseUploadFile baseFile = mBaseImageService.selectOne(fileSign);
			if (baseFile == null || !FileUtil.exist(baseFile.getPath(rootPath))) {
				if (baseFile != null) {
					mBaseImageService.delete(rootPath, fileSign);
				}

				// 构建文件信息对象
				baseFile = new BaseUploadFile();
				baseFile.setCode(fileSign);
				baseFile.setName(fileName);
				baseFile.setLength(file.getSize());
				baseFile.setCreateTime(new Date());
				baseFile.setPath(savePath + fileSign);
				baseFile.setMimeType(file.getContentType());

				// 移动文件到对应目录
				if (FileUtil.writeFromStream(file.getInputStream(), baseFile.getPath(rootPath)) != null) {
					if (mBaseImageService.insert(baseFile) != null) {
						return renderSuccess(baseFile.getCode(), model, "上传成功");
					} else {
						return renderFail(model, "SaveErr", "保存文件信息失败");
					}
				} else {
					return renderFail(model, "MoveErr", "移动文件目录失败");
				}
			} else {
				return renderSuccess(baseFile.getCode(), model, "上传成功");
			}
		} catch (Exception e) {
			return renderFail(model, ExceptionUtil.getCode(e), ExceptionUtil.getMessage(e));
		}
	}

	private static String getRootPath(HttpServletRequest request) {
		return request.getServletContext().getRealPath("/");
	}

	private static String getRealPath(HttpServletRequest request) {
		return "/upload/files/" + df.format(new Date()) + "/";
	}
}
